package com.hardson.toolkit.util;

import lombok.NonNull;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;

public class NetworkUtil {
    /**
     * 根据网卡配置获取IP地址
     *
     * @return String
     */
    public static String getIp() {

        try {
            Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();

            while (netInterfaces.hasMoreElements()) {
                NetworkInterface ni = netInterfaces.nextElement();
                Enumeration<InetAddress> e = ni.getInetAddresses();
                while (e.hasMoreElements()) {
                    InetAddress ip = e.nextElement();
                    if (!ip.isLoopbackAddress() && !ip.getHostAddress().contains(":")) {
                        return ip.getHostAddress();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 获取复杂网络环境下的Ip地址
     *
     * @return InetAddress
     * @throws Exception 异常
     */
    public InetAddress getLocalHostLANAddress() throws Exception {
        try {
            InetAddress candidateAddress = null;
            // 遍历所有的网络接口
            Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
            while (ifaces.hasMoreElements()) {
                NetworkInterface iface = ifaces.nextElement();
                // 在所有的接口下再遍历IP
                Enumeration<InetAddress> inetAddrs = iface.getInetAddresses();
                while (inetAddrs.hasMoreElements()) {
                    InetAddress inetAddr = inetAddrs.nextElement();
                    if (!inetAddr.isLoopbackAddress()) {// 排除loopback类型地址
                        if (inetAddr.isSiteLocalAddress()) {
                            // 如果是site-local地址，就是它了
                            return inetAddr;
                        } else if (candidateAddress == null) {
                            // site-local类型的地址未被发现，先记录候选地址
                            candidateAddress = inetAddr;
                        }
                    }
                }
            }
            if (candidateAddress != null) {
                return candidateAddress;
            }
            // 如果没有发现 non-loopback地址.只能用最次选的方案
            return InetAddress.getLocalHost();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String getMac() throws SocketException {
        Enumeration<NetworkInterface> el = NetworkInterface.getNetworkInterfaces();
        while (el.hasMoreElements()) {
            String resolvedMac = generateMac(el.nextElement().getHardwareAddress());
            if (!resolvedMac.isEmpty()) {
                return resolvedMac;
            }
        }
        return "";
    }

    /**
     * 获取所有的网卡MAC地址
     *
     * @return mac地址列表
     * @throws SocketException Socket异常
     */
    public static List<String> getAllMacList() throws SocketException {
        Enumeration<NetworkInterface> el = NetworkInterface.getNetworkInterfaces();
        List<String> macList = new ArrayList<String>();
        while (el.hasMoreElements()) {
            String resolvedMac = generateMac(el.nextElement().getHardwareAddress());
            if (!resolvedMac.isEmpty()) {
                macList.add(resolvedMac);
            }
        }
        return macList;
    }

    /**
     * 检查MAC地址是否存在于本机
     *
     * @param macAddr 以'-'分隔的网卡MAC地址
     * @return boolean
     * @throws SocketException Socket异常
     */
    public static boolean checkMac(final String macAddr) throws SocketException {
        Enumeration<NetworkInterface> el = NetworkInterface.getNetworkInterfaces();
        while (el.hasMoreElements()) {
            String resolvedMac = generateMac(el.nextElement().getHardwareAddress());
            if (resolvedMac.isEmpty()) {
                continue;
            }
            if (macAddr.equalsIgnoreCase(resolvedMac)) {
                return true;
            }
        }
        return false;
    }

    private static String generateMac(byte[] mac) {
        if (mac == null || mac.length == 0) {
            return "";
        }
        StringBuilder builder = new StringBuilder();
        for (byte b : mac) {
            int val = b;
            if (b < 0) {
                val = 256 + b;
            }
            String sn = Integer.toHexString(val);
            builder.append(sn);
            builder.append("-");
        }
        builder.deleteCharAt(builder.length() - 1);
        return builder.toString();
    }

    /**
     * 判断是否为数字
     *
     * @param str 被检查的字符串
     * @return boolean
     */
    public static boolean isNumerics(String str) {
        if (str == null || (str = str.trim()).isEmpty()) {
            return false;
        }
        return Pattern.matches("[0-9]+", str);
    }

    public static boolean isHost(String ip) {
        if ("localhost".equalsIgnoreCase(ip) || "127.0.0.1".equals(ip)) {
            return true;
        }
        String pattern = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$";
        return Pattern.matches(pattern, ip);
    }

    public static boolean isPort(String portStr) {
        if (!isNumerics(portStr)) {
            return false;
        }
        int port = Integer.parseInt(portStr);
        return (port >= 0 && port <= 65535);
    }

    /**
     * 获取请求客户端IP地址
     * 获取顺序：
     * 1、代理：header->X-Forwarded-For或x-forwarded-for或X-FORWARDED-FOR
     * 2、代理：header->Proxy-Client-IP或proxy-client-ip或PROXY-CLIENT-IP
     * 3、代理：header->WL-Proxy-Client-IP或wl-proxy-client-ip或WL-PROXY-CLIENT-IP
     * 4、代理：header->X-Real-IP或x-real-ip或X-REAL-IP
     * 如果有多重代理以“,”相隔，取第一个；如果取到的值为"unknown"继续尝试取值
     * 5、通过request.getRemoteAddr()获取IP，如果值为"127.0.0.1"或"0:0:0:0:0:0:0:1"根据网卡取本机配置的IP
     *
     * @param request 请求对象
     * @return String
     */
    public static String getRemoteAddr(HttpServletRequest request) {
        String ipAddress = getIpInHeader(request);
        if (!ipAddress.isEmpty() && !isUnKnownFlag(ipAddress)) {
            return checkMultiProxy(ipAddress);
        }
        ipAddress = request.getRemoteAddr();
        if ("127.0.0.1".equals(ipAddress) || "0:0:0:0:0:0:0:1".equals(ipAddress)) {
            try {
                // 根据网卡取本机配置的IP
                ipAddress = InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
        }
        return ipAddress;
    }

    private static String getIpInHeader(HttpServletRequest request) {
        String addr = Objects.toString(request.getHeader("X-Forwarded-For"), "").trim();
        if (!addr.isEmpty()) {
            return addr;
        }
        addr = Objects.toString(request.getHeader("x-forwarded-for"), "").trim();
        if (!addr.isEmpty()) {
            return addr;
        }
        addr = Objects.toString(request.getHeader("X-FORWARDED-FOR"), "").trim();
        if (!addr.isEmpty()) {
            return addr;
        }
        addr = Objects.toString(request.getHeader("Proxy-Client-IP"), "");
        if (!addr.isEmpty()) {
            return addr;
        }
        addr = Objects.toString(request.getHeader("proxy-client-ip"), "");
        if (!addr.isEmpty()) {
            return addr;
        }
        addr = Objects.toString(request.getHeader("PROXY-CLIENT-IP"), "");
        if (!addr.isEmpty()) {
            return addr;
        }
        addr = Objects.toString(request.getHeader("WL-Proxy-Client-IP"), "");
        if (!addr.isEmpty()) {
            return addr;
        }
        addr = Objects.toString(request.getHeader("wl-proxy-client-ip"), "");
        if (!addr.isEmpty()) {
            return addr;
        }
        addr = Objects.toString(request.getHeader("WL-PROXY-CLIENT-IP"), "");
        if (!addr.isEmpty()) {
            return addr;
        }
        addr = Objects.toString(request.getHeader("X-Real-IP"), "");
        if (!addr.isEmpty()) {
            return addr;
        }
        addr = Objects.toString(request.getHeader("x-real-ip"), "");
        if (!addr.isEmpty()) {
            return addr;
        }
        return Objects.toString(request.getHeader("X-REAL-IP"), "");
    }

    /**
     * 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
     *
     * @param ipAddress ip地址
     * @return String
     */
    private static String checkMultiProxy(@NonNull String ipAddress) {
        // "***.***.***.***".length() = 15
        if (ipAddress.length() > 15) {
            int idx = ipAddress.indexOf(",");
            if (idx != -1) {
                return ipAddress.substring(0, idx);
            }
        }
        return ipAddress;
    }

    private static boolean isUnKnownFlag(String str) {
        return "unknown".equalsIgnoreCase(str);
    }
}
